■ ウォッチドックタイマ
ノイズでマイコンのプログラムカウンタが書き換わるとプログラムがとんでもない番地にとんでいってしまい暴走が発生
します。暴走が発生した場合ポートは0か1になってしまうことが多いようです。昔のことですが、暴走した工作ロボット
が作業者を死亡させてしまった記事が新聞に大きくとりあげられていたことをよく覚えています。マイコンを誤動作させる
外乱としては、静電気ノイズ、電源ラインからの伝導EMIノイズ、バーストノイズ、放射EMIノイズ、雷サージノイズ、ディッ
プ等いろいろあります。 また、接触不良、電子部品の不良、塵埃、結露といった要因でも暴走は発生することがありま
す。 制御対象がクリティカルな場合だけでなくてもウォッチドックタイマを使用した設計の習慣を身につけたいものです。
ウォッチドック(番犬)タイマとはマイコンが暴走したことをハード的に検出してリセットをおこなうハードのタイマ回路で
す。使用方法は、
@ タイマ時間の設定をおこなう
A ウォッチドックタイマがタイムアウトしないうちに定期的にタイマのリセットをおこなう
B ウォッチドックタイマが動作した場合の処理をきめる。
@ : タイマ時間の上限はマイコンが暴走した場合の危害程度と時間の関係から決定します。工作ロボットなどのアク
チュエータが1秒間もの長い時間勝手に動いた場合は極めて危険なことが発生します。 設計的に難しくないならできる
だけ短い時間に設定します。PICの場合最小が18msecです。プリスケーラの設定により36msec、72msec、144msec、
288msec、576msec、1152msec、2304msecが選択できます。
A : ウォッチドックタイマのリセット方法
組込マイコンの場合処理ルーチンが繰り返されているのがふつうですので、以下のような方法があります。
(1) インターバルタイマをつかって定期的にリセットをおこなう
(2) 繰り返しルーチンのなかで1回リセットをおこなう
(ルーチンの最悪処理時間がウォッチドックタイマのタイムアウト時間に対して十分短い場合)
(3) 繰り返しルーチンのなかで複数回に分けてリセットをおこなう
★ルーチンの中で1回だけのリセットではウォッチドックタイマがタイムアウトしてしまう場合、音声制御などで
インタ-バルタイマを使かうとノイズ音がでたりするなどでインターバルタイマが使用できない場合及び
割込みが別の目的使用されている為インターバルタイマが使用できない場合など好ましいものでは
ありませんが実際の設計では多々遭遇します。
B : ウォッチドックタイマが動作した場合の処理
電源を投入した状態に戻すのが一般的ですが、ウォッチドックタイマが動作したことを表示してシステムを停止
させておくといった処理もあります。 また、システムによっては再び誤動作が発生する直前と同じ状態でのシステム
再稼動をおこなう場合があります。この場合誤動作発生の直前のシステムの状態を記憶しておく必要があります。
記憶の方法としてEEPROMに記憶しておいたり、またスイッチはモメンタリースイッチはやめてオルタネートスイッチに
しておくといった方法があります。
ウォッチドックタイマのリセット方法違いでの例を、以下に紹介します。
(1) インターバルタイマをつかって定期的にウォッチドックタイマのリセットをおこなう
<試作品仕様>
・電源を投入した状態ではRB0〜RB7のLEDはすべて消灯している
・モーメンタリースイッチを押すととRB0〜RB7のLEDはON時間1秒、OFF時間1秒のフリッカーを繰り返す
・モーメンタリースイッチを押すとRB0〜RB7のLEDはすべて消灯する
・ウォッチドックタイマが動作した場合はRB0〜RB3は点灯、RB4〜RB7は消灯した状態となり、スイッチを押しても
LEDは動作しない
・ウォッチドックタイマの設定時間は36msecとする
<試作品回路図>(→回路図のPDFファイル)
PIC18F452をつかった場合の回路図を以下に示します。外付けの水晶発振子の周波数は10MHzですがPIC内部の
PLL回路で4倍にしているため内部動作クロック周波数は40MHzです。
<試作品外観> 下記の写真には上記回路図にはない、また本テーマと関係のない部品も多々写っています
<プログラム例> #include <18F452.h> #use delay(clock=40000000) // clock = Crystal OSC clock(10MHz) × Multiple of PLL(4) #FUSES H4,PUT,WDT2,BROWNOUT,BORV42,NOPROTECT,NOLVP // H4 : PLL Mode at HS mode // WDT2 : ウォッチドックタイマ時間=36msec ( = 18msec×2 ) #byte port_B=0x0f81 #define ON 1 #define OFF 0 short int Led = 0; int SwCount = 0; #int_timer1 void interval() //10msec毎の割込み { set_timer1(53036); // 256*256 -12500 = 53036 // 0.025 usec * 4 * 8 * 12500 = 10000 usec =10 msec restart_wdt();// WDT timer reset //ウォッチドックタイマをリセット if(input(PIN_A4) == 0)SwCount++; //スイッチが押されていたならば else SwCount = 0; // スイッチが押されていなかったらリセット if(SwCount == 10) //10msec毎にチェックして10回スイッチがONであればスイッチが押されたと判断する { if(Led == OFF)Led = ON; else Led = OFF; } if(SwCount >= 11) SwCount = 11; } main() { set_tris_b(0); // Cポートをすぺて出力モードに固定 setup_timer_1(T1_INTERNAL |T1_DIV_BY_8); //タイマ1のプリスケーラを1/8にセット set_timer1(53036); enable_interrupts(INT_TIMER1); enable_interrupts(GLOBAL); switch(restart_cause()) { case WDT_TIMEOUT: // ウォッチドックタイマが働いた場合 port_B = 0xf0; //下位4ビットのみLEDをONする goto end; // プログラム終了 break; case NORMAL_POWER_UP: //通常のマイコン立ち上がり場合 port_B = 0xff; break; default: break; } while(1) { if(Led == ON) //LEDフリッカーモード(1秒ON 1秒OFF) { port_B = 0x0000; //LED ON delay_ms(1000); port_B = 0xffff; //LED OFF delay_ms(1000); } else port_B = 0xffff; //LED OFF } end: return 0; }
(1)a 繰り返しルーチンのなかで1回ウォッチドックタイマのリセットをおこなう <C18編> (WDTのクリアをおこなわず、WDTが動作した場合) <試作品仕様> ・プログラム起動後1秒後にLEDを点滅させる。 ・点滅はON時間200msec、OFF時間200msecとする。 ・ウォッチドックタイマのタイム(WDT)アウト時間は4096msecとする ・WDTのクリアは実施しない。 <試作品回路図>(→回路図のPDFファイル)
<試作品外観> 下記の写真には上記回路図にはない、また本テーマと関係のない部品も多々写っています
<プログラム例> // WDT 基本動作 #include <p18f87K90.h> #include <stdio.h> #include <delays.h> #include <portb.h> #pragma config FOSC = XT #pragma config XINST = OFF #pragma config WDTEN = ON #pragma config WDTPS = 1024 //ポストスケーラ 1/1024 → 4mse x 1024 = 4096msec //タイムアウト時間 #pragma config SOSCSEL = DIG //Digital mode //SOSCSEL = LOW : Low Power SOSC circuit selected //SOSCSEL = HIGH: High Power SOSC circuit selec void delay_ms(long int cycle) { unsigned long int i = 0; for (i = 0; i < cycle; i++)Delay10TCYx(25); // Delay10TCY():40μsec } void main (void) { TRISG = 0; WDTCONbits.SWDTEN = 1; //ウォッチドックタイマ 動作開始 delay_ms(1000); while (1) { LATGbits.LATG2 = 0; LATGbits.LATG3 = 0; delay_ms(200); LATGbits.LATG2 = 1; LATGbits.LATG3 = 1; delay_ms(200); } }
<動作結果>
以下は、RG2ポートの電圧をデジタルオシロで測定した結果です。 WDTがタイムアウトして
PICがリセットされています。 そしてこれがくりかえされているのがわかります。
(1)b 繰り返しルーチンのなかで1回ウォッチドックタイマのリセットをおこなう <C18編>
(WDTのクリアをおこなった場合)
<試作品仕様>
・プログラム起動後1秒後にLEDを点滅させる。
・点滅はON時間200msec、OFF時間200msecとする。
・ウォッチドックタイマのタイム(WDT)アウト時間は4096msecとする
・WDTのクリア を メインの待ちループ内で 約400msec毎に実施する。
<試作品回路図>(→回路図のPDFファイル)
<試作品外観> 下記の写真には上記回路図にはない、また本テーマと関係のない部品も多々写っています
<プログラム例> // WDT 基本動作 #include <p18f87K90.h> #include <stdio.h> #include <delays.h> #include <portb.h> #pragma config FOSC = XT #pragma config XINST = OFF #pragma config WDTEN = ON #pragma config WDTPS = 1024 //ポストスケーラ 1/1024 → 4mse x 1024 = 4096msec //タイムアウト時間 #pragma config SOSCSEL = DIG //Digital mode //SOSCSEL = LOW : Low Power SOSC circuit selected //SOSCSEL = HIGH: High Power SOSC circuit selec void delay_ms(long int cycle) { unsigned long int i = 0; for (i = 0; i < cycle; i++)Delay10TCYx(25); // Delay10TCY():40μsec } void main (void) { TRISG = 0; WDTCONbits.SWDTEN = 1; //ウォッチドックタイマ 動作開始 delay_ms(1000); while (1) { LATGbits.LATG2 = 0; LATGbits.LATG3 = 0; delay_ms(200); LATGbits.LATG2 = 1; LATGbits.LATG3 = 1; delay_ms(200); ClrWdt(); //ウォッチドックタイマ クリア } }
<動作結果>
400msec毎にWDTをクリアしているので、LEDの点滅は WDTにより停止させられることなく行われています。
(2) 繰り返しルーチンのなかで1回ウォッチドックタイマのリセットをおこなう <CCS編>
<試作品仕様>
<試作品回路図>
<試作品外観> はすべて(1)と同じです
<プログラム例> #include <18F452.h> #use delay(clock=40000000) // clock = Crystal OSC clock(10MHz) × Multiple of PLL(4) #FUSES H4,PUT,WDT2,BROWNOUT,BORV42,NOPROTECT,NOLVP // H4 : PLL Mode at HS mode // WDT2 : ウォッチドックタイマ時間=36msec ( = 18msec×2 ) #byte port_B=0x0f81 #define ON 1 #define OFF 0 short int Led = 0; int SwCount = 0,Led_Count = 0; void SW_Detect() //スイッチ検出 { if(input(PIN_A4) == 0)SwCount++; //スイッチが押されていたならば else SwCount = 0; // スイッチが押されていなかったらリセット if(SwCount == 10) //10msec毎にチェックして10回スイッチがONであればスイッチが押されたと判断する { if(Led == OFF)Led = ON; else Led = OFF; } if(SwCount >= 11) { SwCount = 11; } } void LEDx() //LED ON/OFF制御 { SW_Detect(); //スイッチ検出 if(LED == ON) //LEDフリッカーの場合 { Led_Count++; if(Led_Count <= 100)port_B = 0x00; // 1sec(=10msec×100) LED ON else if(Led_Count <= 200)port_B = 0xff; //1sec(=10msec×100)LED OFF else Led_Count = 0; } else //LED OFFの場合 { port_B = 0xff; //LED OFF Led_Count = 0; } } main() { set_tris_b(0); // Cポートをすぺて出力モードに固定 switch(restart_cause()) { case WDT_TIMEOUT: // ウォッチドックタイマが働いた場合 port_B = 0xf0; //下位4ビットのみLEDをONする goto end; // プログラム終了 break; case NORMAL_POWER_UP: //通常のマイコン立ち上がり場合 port_B = 0xff; break; default: break; } while(1) { LEDx(); //LED ON/OFF制御 delay_ms(10); restart_wdt(); // 10msec毎にウォッチドックタイマをリセット } end: return 0; }
(3) 繰り返しルーチンのなかで複数回に分けてリセットをおこな
(1)(2)がどうしても無理な場合にのみこの方法をとります。
以下の例ではプログラム中の2ヶ所でウォッチドックタイマのリセットはおこなわれています。
<試作品仕様>
<試作品回路図>
<試作品外観> はすべて(1)と同じです
<プログラム例> #include <18F452.h> #use delay(clock=40000000) // clock = Crystal OSC clock(10MHz) × Multiple of PLL(4) #FUSES H4,PUT,WDT2,BROWNOUT,BORV42,NOPROTECT,NOLVP // H4 : PLL Mode at HS mode // WDT2 : ウォッチドックタイマ時間=36msec ( = 18msec×2 ) #byte port_B=0x0f81 #define ON 1 #define OFF 0 short int Led = 0; int SwCount = 0; int SW_Detect() //スイッチ検出 { int Change = 0; //スイッチの状態変化フラグ if(input(PIN_A4) == 0)SwCount++; //スイッチが押されていたならば else SwCount = 0; // スイッチが押されていなかったらリセット if(SwCount == 10) //10msec毎にチェックして10回スイッチがONであればスイッチが押されたと判断する { if(Led == OFF)Led = ON; else Led = OFF; Change = 1; //スイッチの状態が変化した場合→ Change = 1; } if(SwCount >= 11) { SwCount = 11; Change = 0; } return Change; } main() { int i; int Change = 0; // スイッチの状態変化フラグ set_tris_b(0); // Cポートをすぺて出力モードに固定 LED = OFF; switch(restart_cause()) { case WDT_TIMEOUT: // ウォッチドックタイマが働いた場合 port_B = 0xf0; //下位4ビットのみLEDをONする goto end; //プログラム終了 break; case NORMAL_POWER_UP: //通常のマイコン立ち上がり場合 port_B = 0xff; break; default: break; } while(1) { if(LED == ON)port_B = 0x00; //LED ON else port_B = 0xff; //LED OFF for(i = 0; i < 100; i++) { Change = SW_Detect(); if(Change == 1)break; // スイッチが押された場合は すぐにLEDをOFFにすべく breakでfor ループを抜ける delay_ms(10); restart_wdt(); // 10msec毎にウォッチドックタイマをリセット } port_B = 0xff; //LED OFF for(i = 0; i < 100; i++) { Change = SW_Detect(); if(Change == 1)break;// スイッチが押された場合は すぐにLEDをONにすべく breakでfor ループを抜ける delay_ms(10); restart_wdt(); // 10msec毎にウォッチドックタイマをリセット } } end: return 0; }
(4)スリープモードにおける WDTの動作 (a) WDTタイムアウト スリープモードでWDTがタイムアウトした場合は、通常モードのようにPICはリセットされないで ウェークアップするだけです。 ウェークアップ後は、Sleep( )命令の後の命令からプログラムが再開されます。 <試作品仕様> ・プログラム起動後の1秒後にLEDを点滅させる。 ・点滅はON時間200msec、OFF時間200msecとする。 ・ウォッチドックタイマのタイム(WDT)アウト時間は4096msecとする ・WDTのクリアは実施しない。 ・スリープしたWDTがウェークアップしたらRJ0ポートののLEDを1秒間点灯させる。 ・RJ0のLEDを消灯後は、再びスリープする。 以後これを繰り返す。
<試作品回路図>(→回路図のPDFファイル)
<試作品外観> 下記の写真には上記回路図にはない、また本テーマと関係のない部品も多々写っています
<プログラム例> // WDT //スリープ移行後、 タイムアウトしてLEDを1秒間点灯、 その後再びスリープへ移行。 以後この繰り返し #include <p18f87K90.h> #include <stdio.h> #include <delays.h> #include <portb.h> #pragma config FOSC = XT #pragma config XINST = OFF #pragma config WDTEN = ON #pragma config WDTPS = 1024 //ポストスケーラ 1/1024 → 4mse x 1024 = 4096msec //タイムアウト時間 #pragma config SOSCSEL = DIG //Digital mode //SOSCSEL = LOW : Low Power SOSC circuit selected //SOSCSEL = HIGH: High Power SOSC circuit selec int Flag_first = 1; void delay_ms(long int cycle) { unsigned long int i = 0; for (i = 0; i < cycle; i++)Delay10TCYx(25); // Delay10TCY():40μsec } void main (void) { int i; TRISG = 0; TRISJ = 0; LATJbits.LATJ0 = 1; //LED OFF WDTCONbits.SWDTEN = 1; //ウォッチドックタイマ 動作開始 delay_ms(1000); while (1) { if(Flag_first == 1) // プログラム起動後の最初だけ RG2、RG3のLEDを6回点滅 { for(i = 0; i < 6; i++) { LATGbits.LATG2 = 0; LATGbits.LATG3 = 0; delay_ms(200); LATGbits.LATG2 = 1; LATGbits.LATG3 = 1; delay_ms(200); ClrWdt(); //ウォッチドックタイマ クリア } } Flag_first = 0; Sleep(); //スリープモードに移行 LATJbits.LATJ0 = 0; //★ スリープモードでWDTがタイムアウトした場合は 次の命令であるこの命令から始まる //約4-5秒後、WDTタイムアウト発生 → RJ0のLED点灯 //リセットはかからずに、ウェイクして 次のステップの命令を実行 delay_ms(1000); // 1秒間点灯後 LED消灯 LATJbits.LATJ0 = 1; } } <動作結果> 4秒スリープ後、ウェークアップしてRJ0ポートのLEDを1秒点灯させる繰り返しをおこなっているWDTの動作が確認できます。
上: RJ0ポートの電圧波形 下: RG2ポートの電圧波形 |
(b) タイムアウト後、リセット実施
スリープモードでWDTがオーバーフローした場合は、通常モードのようにPICはリセットされないで ウェィクアップするだけです。 ウェィクアップ直後にリセットを行った場合です。
<試作品仕様>
・プログラム起動後の1秒後にLEDを点滅させる。
・点滅はON時間200msec、OFF時間200msecとする。
・ウォッチドックタイマのタイム(WDT)アウト時間は4096msecとする
・WDTのクリアは実施しない。
・スリープしたWDTがウェークアップしたら リセットを実施する。
<試作品回路図>(→回路図のPDFファイル)
<試作品外観> 下記の写真には上記回路図にはない、また本テーマと関係のない部品も多々写っています
<プログラム例> // WDT //スリープ移行後、 タイムアウトしてLEDを1秒間点灯、 その後再びスリープへ移行。 以後この繰り返し #include <p18f87K90.h> #include <stdio.h> #include <delays.h> #include <portb.h> #pragma config FOSC = XT #pragma config XINST = OFF #pragma config WDTEN = ON #pragma config WDTPS = 1024 //ポストスケーラ 1/1024 → 4mse x 1024 = 4096msec //タイムアウト時間 #pragma config SOSCSEL = DIG //Digital mode //SOSCSEL = LOW : Low Power SOSC circuit selected //SOSCSEL = HIGH: High Power SOSC circuit selec int Flag_first = 1; void delay_ms(long int cycle) { unsigned long int i = 0; for (i = 0; i < cycle; i++)Delay10TCYx(25); // Delay10TCY():40μsec } void main (void) { int i; TRISG = 0; TRISJ = 0; LATJbits.LATJ0 = 1; //LED OFF WDTCONbits.SWDTEN = 1; //ウォッチドックタイマ 動作開始 delay_ms(1000); while (1) { if(Flag_first == 1) // プログラム起動後の最初だけ RG2、RG3のLEDを6回点滅 { for(i = 0; i < 6; i++) { LATGbits.LATG2 = 0; LATGbits.LATG3 = 0; delay_ms(200); LATGbits.LATG2 = 1; LATGbits.LATG3 = 1; delay_ms(200); ClrWdt(); //ウォッチドックタイマ クリア } } Flag_first = 0; Sleep(); //スリープモードに移行 Reset(); //ウェイクアップしたらリセット } }
<動作結果>
RG2ポートの波形です。 スリープから ウェイク直後リセットがかかり、プログラムが再起動していることが確認できます。
(5) スリープモードでの SOSCによるWDTクリア
通常、スリープモードの場合 WDTなども含め全クロックが停止させます。 したがって WDTをクリアする必要はありません。 但し ほとんどのクロックがクロックが停止してしまいますが、
スリープ中でも動作させることができるクロックとして、SOSCがあります。 スリープモードでSOSCを使った制御をおこなった場合 WDTがタイムアウトしても リセットがかからずウェークするだけとなり
通常の場合とWDTの挙動と異なります。 WDTがタイムタイムアウトした場合にPICをリセットするには RCONレジスタのTOフラグ( WDT
timeout Flag)をチェックしてリセットします。
<試作品仕様>
・プログラムスタート時 2個のLEDをリセット発生確認用として 1回点滅させる
・WDTのタイムアウト時間を4秒にセットする
・タイマ1の クロックとしてSOSC(32.768KHz)を用いる。
・タイマ1で 2秒間隔のタイマ割り込みを発生させ、このなかでWDTのクリア 及び 割り込み発生確認用としてLEDを1回点滅させる
・ウェークした場合、RCONレジスタの TOフラグ(タイムアウト検出)をチェックして タイムアウトの場合は PICをリセットする。
<試作品回路図>(→回路図のPDFファイル)
<試作品外観> 下記の写真には上記回路図にはない、また本テーマと関係のない部品も多々写っています
<プログラム例> #include <p18f87K90.h> #include <stdio.h> #include <delays.h> #include <portb.h> #pragma config FOSC = XT #pragma config XINST = OFF #pragma config WDTEN = ON #pragma config WDTPS = 1024 //postscalor 1/1024 → 4mse x 1024 = 4096msec //timeout void Interval(void); void total_INT(void); void delay_ms(long int cycle) //msec delay { unsigned long int i = 0; for (i = 0; i < cycle; i++)Delay10TCYx(20); } #pragma code low_vector=0x08 // void low_interrupt (void) { _asm goto total_INT _endasm } #pragma code #pragma interruptlow total_INT void total_INT() { if(PIR1bits.TMR1IF == 1)Interval(); } void Interval() { PIR1bits.TMR1IF = 0; //flag clear ClrWdt(); // WDT clear //ClrWdt()がなくともリセットは発生しない //タイマ割りこみがないとリセットは発生する。 ウェイクでWDTがクリアされる? WriteTimer1(0x7FFE); //31μsec x (0xFFFF - 0x7FFE) = 31 x (65536-32766) //= 1sec LATJbits.LATJ0 = 0; // LED ON delay_ms(100); LATJbits.LATJ0 = 1; //LED OFF } void main (void) { TRISJ = 0; TRISG = 0; T1CONbits.TMR1CS1 = 1; //b7:SOSC T1CONbits.TMR1CS0 = 0; //b6: T1CONbits.T1CKPS1 = 0; //b5: prescalor 1/1 T1CONbits.T1CKPS0 = 0; //b4: T1CONbits.SOSCEN = 1; //b3: SOSC: enable // T1CONbits.T1SYNC = 1; //b2: Do not synchronize the external clock input T1CONbits.RD16 = 1; //b1: Read/Write mode: 16bit T1CONbits.TMR1ON = 1; //b0: Timer1 ON T1CON = 0b10001111; LATGbits.LATG2 = 1; LATGbits.LATG3 = 1; delay_ms(200); LATGbits.LATG2 = 0; //LED ON LATGbits.LATG3 = 0; //LED OFF delay_ms(200); LATGbits.LATG2 = 1; LATGbits.LATG3 = 1; delay_ms(200); WDTCONbits.SWDTEN = 1; //WDT on RCONbits.IPEN = 0; PIE1bits.TMR1IE = 1; // Timer innterrupt enable INTCONbits.PEIE =1; INTCONbits.GIE = 1; while (1) { Sleep(); if(RCONbits.TO == 0)Reset(); // TO: WDT timeout Flag } }
<動作結果>
スリープ中の RJ0ポートの電圧波形です。 1sec毎に割り込みが発生していることが確認できます。